//
// Created by l00391713 on 2024/3/5.
//
#include <memory>
#include <random>

#include "gtest.h"
#include "ParallelTcpStore.hpp"

using namespace c10d;
using namespace c10d::pta;

class ParallelTcpStoreTest : public testing::Test {
public:
    ParallelTcpStoreTest() noexcept : portOffset_{ 0 }
    {
        std::random_device rd;
        std::mt19937 gen(rd());
        std::uniform_int_distribution<uint16_t> randU16(10000, 15000);
        beginPort_ = randU16(gen);
    }

    void SetUp() override
    {
        portOffset_++;

        TCPStoreOptions options{};
        options.isServer = true;
        options.port = beginPort_ + portOffset_;
        options.waitWorkers = false;
        options.multiTenant = true;
        server_ = std::make_shared<ParallelTcpStore>("127.0.0.1", options);

        options.isServer = false;
        client_ = std::make_shared<ParallelTcpStore>("127.0.0.1", options);
    }

    void TearDown() override
    {
        client_.reset();
        server_.reset();
    }

protected:
    uint16_t beginPort_;
    uint16_t portOffset_;
    std::shared_ptr<Store> server_;
    std::shared_ptr<Store> client_;
};

TEST_F(ParallelTcpStoreTest, client_set_and_server_get)
{
    std::string key = "key/ParallelTcpStoreTest/client_set_and_server_get";
    std::string value = "value/ParallelTcpStoreTest/client_set_and_server_get";
    auto begin = reinterpret_cast<const uint8_t *>(value.data());
    client_->set(key, std::vector<uint8_t>{begin, begin + value.size()});

    auto result = server_->get(key);
    auto rs = std::string{result.begin(), result.end()};
    EXPECT_EQ(value, rs);
}

TEST_F(ParallelTcpStoreTest, client_server_add)
{
    std::string key = "key/ParallelTcpStoreTest/client_server_add";
    auto expect = 1L;
    auto value = client_->add(key, 1);
    EXPECT_EQ(expect, value);

    for (auto i = 0; i < 100; i++) {
        expect++;
        value = server_->add(key, 1);
        EXPECT_EQ(expect, value);

        expect++;
        value = client_->add(key, 1);
        EXPECT_EQ(expect, value);
    }
}

TEST_F(ParallelTcpStoreTest, set_delete_key_and_key_count)
{
    std::string keyBase = "key/ParallelTcpStoreTest/set_delete_key_and_key_count";
    std::string valueBase = "value/ParallelTcpStoreTest/set_delete_key_and_key_count";
    std::vector<std::string> keys;

    for (auto i = 0; i < 100; i++) {
        auto key = keyBase + "/" + std::to_string(i);
        auto value = valueBase + "/" + std::to_string(i);
        auto begin = reinterpret_cast<const uint8_t *>(value.data());
        auto bytes = std::vector<uint8_t>{begin, begin + value.size()};
        keys.emplace_back(key);

        auto keysCount = server_->getNumKeys();
        client_->set(key, bytes);
        auto keyCount2 = server_->getNumKeys();
        EXPECT_EQ(keysCount + 1L, keyCount2);
    }

    for (auto &key : keys) {
        auto keysCount = server_->getNumKeys();
        client_->deleteKey(key);
        auto keyCount2 = server_->getNumKeys();
        EXPECT_EQ(keysCount - 1L, keyCount2);
    }
}

TEST_F(ParallelTcpStoreTest, multi_server_set_get)
{
    TCPStoreOptions options{};
    options.isServer = true;
    options.port = beginPort_ + portOffset_;
    options.waitWorkers = false;
    options.multiTenant = true;
    auto server = std::make_shared<ParallelTcpStore>("127.0.0.1", options);
    auto value = server->add("multi-server-key", 10);
    auto value1 = server_->add("multi-server-key", 0);
    EXPECT_EQ(value, value1);
}

